package graphing;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 *
 * Fully Tested: No
 * Fully Documented: No
 * Created: 29-May-2012
 * Last Updated: 29-May-2012
 *
 * @author Shimu Wu
 */
public class Parser {
    
    
    /*
     * Greedy   Reluctant  Possessive
     * X?       X??         X?+ 	X, once or not at all
     * X*       X*?         X*+ 	X, zero or more times
     * X+       X+?         X++ 	X, one or more times
     */
    private final Pattern 
            // Infix operators
            add = Pattern.compile("^([^+]++)+(.++)$"),
            subtract = Pattern.compile("^([^-]++)-(.++)$"),
            multiply = Pattern.compile("^([^\\*]++)\\*(.++)$"),
            divide = Pattern.compile("^([^/]++)/(.++)$"),
    
            // Prefix operators
            sine = Pattern.compile("^sin(.++)$"),
            cosine = Pattern.compile("^cos(.++)$"),
            naturalLog = Pattern.compile("^ln(.++)$"),
            log = Pattern.compile("^log(.++)$"),
            
            num = Pattern.compile("^-?+[0-9]*\\.?+[0-9]++$");
            
    public Parser(){
        
    }
    
    public double parse(String expr) {
        expr = expr.trim().toLowerCase();
        System.out.println(expr);
        
        // Matches numbers
        Matcher m = num.matcher(expr);
        if (m.matches()){ 
            return Double.parseDouble(expr);
        }
        
        // ========== Mathematical Constants ==========
        
        // Pi (3.14159)
        if (expr.equals("pi") || expr.equals("π")) {
            return Math.PI;         
            
        // E (2.71828)
        } else if (expr.equals("e")) {
            return Math.E;
        }
        
        // ========== Infix Operators ==========
        
        // Multiplication
        m = multiply.matcher(expr);
        if (m.matches()) {
            return parse(m.group(1)) * parse(m.group(2));
        }
        
        // Division
        m = divide.matcher(expr);
        if (m.matches()) {
            return parse(m.group(1)) / parse(m.group(2));
        }
        
        // Addition
        m = add.matcher(expr);
        if (m.matches()) {
            return parse(m.group(1)) + parse(m.group(2));
        }
        
        // Subtraction
        m = subtract.matcher(expr);
        if (m.matches()) {
            return parse(m.group(1)) - parse(m.group(2));
        }
        
        // ========== Prefix Operators ==========
        // Sine
        m = sine.matcher(expr);                
        if (m.matches()) {
            return Math.sin(parse(m.group(1)));
        } 
        
        // Cosine
        m = cosine.matcher(expr);        
        if (m.matches()) {
            return Math.cos(parse(m.group(1)));
        }
        
        // Natural log (ln)
        m = naturalLog.matcher(expr);        
        if (m.matches()) {
            return Math.log(parse(m.group(1)));
        }
        
        // Base 10 Logarithm (log)
        m = log.matcher(expr);        
        if (m.matches()) {
            return Math.log10(parse(m.group(1)));
        }
        
        return 0;
    }
}
